﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Windows.Forms;
using iTunesLib;
using System.Xml;
using System.Xml.Linq;
using System.Text;
using System.IO;

namespace iTunes_Playcount_Updater_GUI
{
    internal sealed partial class Form1 : Form
    {
        const string ApiKey = "49f694be0c92a2719c51ef324c57a60d";
        internal static Form1 StaticVar;
        internal String Username;
        readonly List<TrackReport> _trackReports = new List<TrackReport>();
        readonly List<LastFmTrack> _tracks = new List<LastFmTrack>();
        private iTunesApp _iTunes;
        private long _ticksBefore;
        private long _ticksNow;
        private TimeSpan _timeSpan;
        public delegate void DownloadPagesCallback();
        public delegate void UpdateReadyCallback(Thread thread);

        internal Form1()
        {
            InitializeComponent();
        }

        private void Form1Shown(object sender, EventArgs e)
        {
            StaticVar = this;
            panel1.Controls.Add(new OpeningiTunes());
            _iTunes = new iTunesApp();
            panel1.Controls.Clear();
            panel1.Controls.Add(new LastFmLogin());
        }

        internal void LastFmDownload()
        {
            panel1.Controls.Clear();
            panel1.Controls.Add(new DownloadingPages());
            var downloadingPages = (DownloadingPages)panel1.Controls[0];
            const string requestUrlBase = "http://ws.audioscrobbler.com/2.0/?method=library.gettracks&api_key={0}&user={1}&page={2}";
            _ticksBefore = DateTime.Now.Ticks;
            using (var client = new WebClient())
            {
                var page = 1;
                var reader = XmlReader.Create(client.OpenRead(string.Format(requestUrlBase, ApiKey, Username, page)));
                Int32 pages;
                reader.ReadToFollowing("lfm");
                if (reader.GetAttribute("status") == "ok")
                {
                    reader.ReadToFollowing("tracks");
                    pages = Convert.ToInt32(reader.GetAttribute("totalPages"));
                    downloadingPages.progressBar1.Maximum = pages;
                    AddTracks(reader);
                    downloadingPages.progressBar1.Value = 1;
                }
                else
                {
                    panel1.Controls.Clear();
                    panel1.Controls.Add(new LoginError { textBox1 = { Text = Username } });
                    reader.Close();
                    return;
                }
                AddTracks(XmlReader.Create(client.OpenRead(String.Format(requestUrlBase, ApiKey, Username, page+1))));
                ThreadStart threadstart = () =>
                {
                    for (page = 2; page <= pages; page++)
                    {
                        var page1 = page;
                        Form1.StaticVar.AddTracks(XmlReader.Create(new WebClient().OpenRead(String.Format(requestUrlBase, ApiKey, Username, page1))));
                        downloadingPages.Invoke(new DownloadPagesCallback(DownloadPages));
                    }
                    Invoke(new UpdateReadyCallback(UpdateReady), new object[] { Thread.CurrentThread });
                };
                var thread = new Thread(threadstart);
                thread.Start();
            }
        }

        private void DownloadPages()
        {
            ((DownloadingPages)panel1.Controls[0]).progressBar1.PerformStep();
        }

        private void UpdateReady(Thread thread)
        {
            thread.Abort();
            UpdateiTunes();
        }

        internal void AddTracks(XmlReader reader)
        {
            while (reader.Read())
            {
                reader.ReadToFollowing("track");
                if (reader.NodeType == XmlNodeType.None)
                    break;
                var subtree = reader.ReadSubtree();
                subtree.ReadStartElement("track");
                var name = subtree.ReadElementString("name");
                var playcount = Convert.ToInt32(subtree.ReadElementString("playcount"));
                subtree.ReadToFollowing("artist");
                subtree.ReadStartElement("artist");
                var artist = reader.ReadElementString("name");
                subtree.Skip();
                subtree.Skip();
                subtree.Skip();
                subtree.Skip();
                subtree.ReadEndElement();
                String album;
                try
                {
                    subtree.ReadStartElement("album");
                    album = subtree.ReadElementString("name");
                }
                catch
                {
                    album = "";
                }
                subtree.Close();
                _tracks.Add(new LastFmTrack {Name = name, Plays = playcount, Artist = artist, Album = album});
                subtree.Close();
            }
            reader.Close();
        }

        private void UpdateiTunes()
        {
            var iTunesTracks = _iTunes.LibraryPlaylist.Tracks;
            var count = iTunesTracks.Count;
            for (var i = 1; i <= count; i++)
            {
                var j = i;
                ThreadPool.QueueUserWorkItem(k =>
                                                 {
                                                     var iTunesTrack = iTunesTracks[j];

                                                     if (iTunesTrack == null) return;
                                                     var lastFmTrack = (from t in _tracks
                                                                                where t.Artist == iTunesTrack.Artist
                                                                                where t.Album == iTunesTrack.Album
                                                                                where t.Name == iTunesTrack.Name
                                                                                select t).FirstOrDefault();
                                                     if (lastFmTrack == null) return;

                                                     if (iTunesTrack.PlayedCount < lastFmTrack.Plays)
                                                     {
                                                         _trackReports.Add(new TrackReport
                                                                               {
                                                                                   Name = lastFmTrack.Name,
                                                                                   Artist = lastFmTrack.Artist,
                                                                                   Album = lastFmTrack.Album,
                                                                                   OldPlayCount =
                                                                                       iTunesTrack.PlayedCount,
                                                                                   NewPlayCount =
                                                                                       lastFmTrack.Plays
                                                                               });
                                                     }
                                                     
                                                     iTunesTrack.PlayedCount = lastFmTrack.Plays;
                                                 });
            }
            _ticksNow = DateTime.Now.Ticks;
            _timeSpan = TimeSpan.FromTicks(_ticksNow - _ticksBefore);
            panel1.Controls.Clear();
            panel1.Controls.Add(new Report());
        }

        internal void Reports()
        {
            panel1.Controls.Clear();
            panel1.Controls.Add(new ReportTypes());
        }

        internal void XmlReport()
        {
            var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
            var root =
                new XElement("updatedtracksreport",
                    new XAttribute("runningtimeinticks", _timeSpan.Ticks),
                    new XAttribute("updatedtrackscount", _trackReports.Count));

            var tracks = from t in _trackReports
                         group t by t.Artist into artists
                         from artist in
                             (from track in artists
                              group track by track.Album)
                         group artist by artists.Key;

            foreach (var artist in tracks)
            {
                var artistElement = new XElement("artist",
                                        new XAttribute("name", artist.Key));
                foreach (var album in artist)
                {
                    XElement albumElement =
                        new XElement("album",
                            new XAttribute("name", album.Key));
                    foreach (var track in album)
                    {
                        albumElement.Add(
                            new XElement("track",
                                new XAttribute("name", track.Name),
                                new XElement("oldplaycount", track.OldPlayCount),
                                new XElement("newplaycount", track.NewPlayCount)));
                    }
                    artistElement.Add(albumElement);
                }
                root.Add(artistElement);
            }
            document.Add(root);
            File.Create(String.Format("{0}\\iTunes Playcount Updater Report {1}{2}{3}.xml", Application.StartupPath, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Year)).Close();
            document.Save(String.Format("{0}\\iTunes Playcount Updater Report {1}{2}{3}.xml", Application.StartupPath, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Year));

            panel1.Controls.Clear();
            panel1.Controls.Add(new ReportSaved());
        }

        internal void TextReport()
        {
            var builder = new StringBuilder();
            using (var writer = new StringWriter(builder))
            {
                writer.WriteLine("{0} Tracks were updated.", _trackReports.Count);
                writer.WriteLine("Total running time: {0}m {1}s {2}ms", _timeSpan.Minutes, _timeSpan.Seconds, _timeSpan.Milliseconds);
                writer.WriteLine();

                var tracks = from t in _trackReports
                             group t by t.Artist into artists
                             from artist in
                                 (from track in artists
                                  group track by track.Album)
                             group artist by artists.Key;

                foreach (var artist in tracks)
                {
                    writer.WriteLine("{0}", artist.Key);
                    foreach (var album in artist)
                    {
                        writer.WriteLine("  {0}", album.Key);
                        writer.WriteLine();
                        foreach (var track in album)
                        {
                            writer.WriteLine("    {0}", track.Name);
                            writer.WriteLine("    Changed playcount from {0} to {1}", track.OldPlayCount, track.NewPlayCount);
                            writer.WriteLine();
                        }
                    }
                }
            }

            File.WriteAllText(String.Format("{0}\\iTunes Playcount Updater Report {1}{2}{3}.txt", Application.StartupPath, DateTime.Now.Month, DateTime.Now.Day, DateTime.Now.Year), builder.ToString());

            panel1.Controls.Clear();
            panel1.Controls.Add(new ReportSaved());
        }
    }
}